home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / aminet / dev / lang / pcq12src.lzh / Source / Declarations.p < prev    next >
Text File  |  1991-04-12  |  17KB  |  738 lines

  1. External;
  2.  
  3. {
  4.     Declarations.p (of PCQ Pascal)
  5.     Copyright (c) 1989 Patrick Quaid
  6.  
  7.     Generally speaking, this module handles the various
  8. declarations.  The major exception to this is doblock(), in main.p,
  9. which might be considered a declaration.
  10. }
  11.  
  12. {$O-}
  13. {$I "Pascal.i"}
  14.  
  15.     Function EnterStandard(    st_Name : String;
  16.                 st_Object : IDObject;
  17.                 st_Type : TypePtr;
  18.                 st_Storage : IDStorage;
  19.                 st_Offset : Integer) : IDPtr;
  20.         external;
  21.     Function EnterSpell(Str : String) : String;
  22.         external;
  23.     Function Match(i : Symbols): boolean;
  24.         external;
  25.     Procedure Error(s : string);
  26.         external;
  27.     Function ConExpr(VAR ConType : TypePtr): Integer;
  28.         external;
  29.     Function AddType(at_Object : TypeObject;
  30.              at_SubType : TypePtr;
  31.              at_Ref : Address;
  32.              at_Upper,
  33.              at_Lower,
  34.              at_Size : Integer) : TypePtr;
  35.         external;
  36.  
  37.     procedure ns;
  38.         external;
  39.     Function TypeCmp(f, s : TypePtr): Boolean;
  40.         external;
  41.     Function FindID(s: string): IDPtr;
  42.         external;
  43.     function CheckID(s : string): IDPtr;
  44.         external;
  45.     Function CheckIDList(s : String; ID : IDPtr) : Boolean;
  46.         external;
  47.     Procedure EnterID(EntryBlock : BlockPtr; ID : IDPtr);
  48.         external;
  49.     procedure NextSymbol;
  50.         external;
  51.     Procedure NeedLeftParent;
  52.         External;
  53.     procedure NeedRightParent;
  54.         external;
  55.     Procedure Mismatch;
  56.         External;
  57.     Procedure DumpLitQ(Start : Integer);
  58.         External;
  59.     Procedure WriteHex(num : Integer);
  60.         External;
  61.     Function TypeCheck(T1, T2 : TypePtr) : Boolean;
  62.         External;
  63.     Function GetLabel : Integer;
  64.         External;
  65.     Function Suffix(Size : Integer) : Char;
  66.         External;
  67.     Procedure PrintLabel(lab : Integer);
  68.         External;
  69.  
  70.  
  71. Function DeclVar(ob : IDObject) : IDPtr;
  72.     forward;
  73.  
  74. Procedure ReformArgs(ProcID : IDPtr);
  75.  
  76. {
  77.     This is the first in a series of routines that assigns the
  78. proper addresses to procedure or function arguments.
  79. }
  80.  
  81. var
  82.     TotalSize    : Integer;
  83.     ID        : IDPtr;
  84. begin
  85.     ID := ProcID^.Param;
  86.     if ProcID^.Level = 1 then
  87.     TotalSize := 8
  88.     else
  89.     TotalSize := 12;
  90.     While ID <> Nil do begin
  91.     if ID^.Object = ValArg then begin
  92.         TotalSize := TotalSize + ID^.VType^.Size;
  93.         if Odd(TotalSize) then
  94.         TotalSize := Succ(TotalSize);
  95.     end else
  96.         TotalSize := TotalSize + 4;
  97.     ID := ID^.Next;
  98.     end;
  99.     ID := ProcID^.Param;
  100.     while ID <> Nil do begin
  101.     if ID^.Object = ValArg then begin
  102.         TotalSize := TotalSize - ID^.VType^.Size;
  103.         if Odd(TotalSize) then begin
  104.         if ID^.VType^.Size = 1 then begin
  105.             ID^.Offset := TotalSize;
  106.             TotalSize := Pred(TotalSize);
  107.         end else begin
  108.             TotalSize := Pred(TotalSize);
  109.             ID^.Offset := TotalSize;
  110.         end;
  111.         end else
  112.         ID^.Offset := TotalSize;
  113.     end else begin { RefArg }
  114.         TotalSize := TotalSize - 4;
  115.         ID^.Offset := TotalSize;
  116.     end;
  117.     ID := ID^.Next;
  118.     end;
  119. end;
  120.  
  121. Function ReformFields(ID : IDPtr) : Integer;
  122.  
  123. {
  124.     ...Determines the proper offsets of the fields, and returns the
  125. total size of the record.
  126. }
  127. var
  128.     TotalSize : Integer;
  129. begin
  130.     TotalSize := 0;
  131.     while ID <> Nil do begin
  132.     if Odd(TotalSize) and (ID^.VType^.Size <> 1) then
  133.         TotalSize := Succ(TotalSize);
  134.     ID^.Offset := TotalSize;
  135.     TotalSize := TotalSize + ID^.VType^.Size;
  136.     ID := ID^.Next;
  137.     end;
  138.     ReformFields := TotalSize;
  139. end;
  140.  
  141. Function GetRange() : TypePtr;
  142. var
  143.     TP        : TypePtr;
  144.     IndexType1,
  145.     IndexType2    : TypePtr;
  146.     Hold,
  147.     Lo, Hi    : Integer;
  148. begin
  149.     New(TP);
  150.     TP^.Object := ob_subrange;
  151.     Lo := ConExpr(IndexType1);
  152.     if not Match(DotDot1) then
  153.     error("expecting '..' here");
  154.     Hi := ConExpr(IndexType2);
  155.     if not TypeCheck(IndexType1, IndexType2) then begin
  156.     Error("Incompatible range types");
  157.     IndexType1 := BadType;
  158.     end;
  159.     if Lo > Hi then begin
  160.     Error("Lower bound greater than upper bound");
  161.     Hold := Hi;
  162.     Hi := Lo;
  163.     Lo := Hold;
  164.     end;
  165.     GetRange := AddType(ob_subrange, IndexType1, IndexType1,
  166.             Hi, Lo, IndexType1^.Size);
  167. end;
  168.  
  169. Function DeclArgs(ob : IDObject) : IDPtr;
  170.     forward;
  171.  
  172. Function ReadRecord(): TypePtr;
  173.  
  174. {
  175.     This just reads a record.
  176. }
  177. var
  178.     Size   : Integer;
  179.     TP     : TypePtr;
  180. begin
  181.     TP := AddType(ob_record, Nil, Nil, 0, 0, 0);
  182.     if TypeID <> Nil then
  183.     TypeID^.VType := TP;
  184.     TP^.Ref := DeclArgs(field);
  185.     if not match(end1) then
  186.     error("Missing END of record");
  187.     TP^.Size := ReformFields(TP^.Ref);
  188.     ReadRecord := TP;
  189. end;
  190.  
  191. Function ReadEnumeration(): TypePtr;
  192.  
  193. {
  194.     This just reads enumerations and assigns them numbers
  195. starting with zero.  The size of an enumerated type is either 1
  196. or two bytes: Enumerations with > 127 items are contained in 2.
  197. }
  198.  
  199. var
  200.     Position : Integer;
  201.     EnumType : TypePtr;
  202.     ID         : IDPtr;
  203. begin
  204.     Position := 0;
  205.     EnumType := AddType(ob_ordinal, Nil, Nil, 0, 0, 0);
  206.     While CurrSym = Ident1 do begin
  207.     if CheckID(SymText) <> Nil then
  208.         Error("Duplicate ID");
  209.     ID := EnterStandard(SymText, constant, EnumType, st_none, Position);
  210.     Position := Succ(Position);
  211.     NextSymbol;
  212.     if CurrSym <> RightParent1 then
  213.         if not Match(Comma1) then
  214.         Error("Missing Comma");
  215.     end;
  216.     if Position <= 256 then    { Position = # of enumerations + 1 }
  217.     EnumType^.Size := 1
  218.     else
  219.     EnumType^.Size := 2;
  220.     NeedRightParent;
  221.     ReadEnumeration := EnumType;
  222. end;
  223.  
  224.     Function ReadType : TypePtr;
  225.     Forward;
  226.  
  227. Function DefineArray : TypePtr;
  228. var
  229.     TP, TP2,
  230.     LastType : TypePtr;
  231.     ID : IDPtr;
  232.  
  233.     Function DeclareDimension : TypePtr;
  234.     var
  235.     TP : TypePtr;
  236.     begin
  237.     TP := GetRange;
  238.     with TP^ do begin
  239.         Ref := SubType;
  240.         Object := ob_array;
  241.         if Match(Comma1) then
  242.         SubType := DeclareDimension
  243.         else
  244.         SubType := Nil;
  245.     end;
  246.     DeclareDimension := TP;
  247.     end;
  248.  
  249.     Procedure FixArraySize(TP : TypePtr);
  250.     begin
  251.     if TP^.Object = ob_array then begin
  252.         FixArraySize(TP^.SubType);
  253.         TP^.Size := TP^.SubType^.Size * (TP^.Upper - TP^.Lower + 1);
  254.     end;
  255.     end;
  256.  
  257. begin
  258.     if Match(LeftBrack1) then begin
  259.     TP := DeclareDimension;
  260.     LastType := TP;
  261.     While LastType^.SubType <> Nil do
  262.         LastType := LastType^.SubType;   { Get the last array dim }
  263.     if not Match(RightBrack1) then
  264.         error("Expecting a right bracket");
  265.     end else if CurrSym = Ident1 then begin
  266.     ID := FindID(SymText);
  267.     NextSymbol;
  268.     if ID = Nil then begin
  269.         error("Unknown ID");
  270.         TP := BadType;
  271.     end else if ID^.Object <> obtype then begin
  272.         error("Expecting a type");
  273.         TP := BadType;
  274.     end else if ID^.VType^.Object <> ob_subrange then begin
  275.         error("Expecting a range");
  276.         TP := BadType;
  277.     end else
  278.         TP := ID^.VType;
  279.     New(TP2);
  280.     TP2^ := TP^;
  281.     TP := TP2;
  282.     TP^.Next := CurrentBlock^.FirstType;
  283.     CurrentBlock^.FirstType := TP;
  284.     LastType := TP;
  285.     end else begin
  286.     error("Expecting range");
  287.     New(TP);
  288.     TP^ := BadType^;
  289.     LastType := TP;
  290.     end;
  291.     TP^.Object := ob_array;
  292.     if not match(of1) then
  293.     error("expecting OF");
  294.     LastType^.SubType := ReadType;
  295.     FixArraySize(TP);
  296.     DefineArray := TP;
  297. end;
  298.  
  299. Function ReadType(): TypePtr;
  300.  
  301. {
  302.     This routine creates and returns a type record produced
  303. by a normal type declaration.  Note that this function is used
  304. almost everywhere a type is called for, so you can use a full type
  305. description in most places.
  306. }
  307.  
  308. var
  309.     TP    : TypePtr;
  310.     ID  : IDPtr;
  311.     Length : Integer;
  312. begin
  313.     if currsym = ident1 then begin
  314.     ID := FindID(symtext);
  315.     if ID = Nil then begin
  316.         Error("Unknown ID");
  317.         TP := BadType;
  318.         NextSymbol;
  319.     end else if ID^.Object = obtype then begin
  320.         TP := ID^.VType;
  321.         NextSymbol;
  322.      {   if Match(LeftBrack1) and (TP = StringType) then begin
  323.         Length := ConExpr(TP);
  324.         if TypeCheck(TP,IntType) then begin
  325.             if Length < 0 then
  326.             Error("String length must be positive");
  327.             TP := AddType(ob_prestring, CharType, Nil, Pred(Length), 0, Length);
  328.         end else begin
  329.             Error("Expecting an integer length");
  330.             TP := BadType;
  331.         end;
  332.         if not Match(RightBrack1) then
  333.             Error("Missing ]");
  334.         end; }
  335.     end else if ID^.Object = constant then
  336.         TP := GetRange()
  337.     else begin
  338.         Error("Expecting a TYPE");
  339.         TP := BadType;
  340.         NextSymbol;
  341.     end;
  342.     end else if (CurrSym = Numeral1) or (CurrSym = Apostrophe1) then
  343.     TP := GetRange()
  344.     else if match(carat1) then begin
  345.     TP := ReadType();
  346.     TP := AddType(ob_pointer, TP, nil, 0, 0, 4);
  347.     end else if match(leftparent1) then
  348.     TP := ReadEnumeration()
  349.     else if match(array1) then
  350.     TP := DefineArray
  351.     else if match(record1) then begin
  352.     TP := ReadRecord();
  353.     end else if match(file1) then begin
  354.     if not match(of1) then
  355.         error("expecting OF");
  356.     TP := ReadType();
  357.     TP := AddType(ob_file, TP, nil, TP^.Size, 0, 32);
  358.     end else begin
  359.     error("unknown type of thing");
  360.     TP := BadType;
  361.     end;
  362.     readtype := TP;
  363. end;
  364.  
  365. Procedure DeclType;
  366.  
  367. {
  368.     This handles a type declaration block.
  369. }
  370. begin
  371.     While CurrSym = ident1 do begin
  372.     if CheckID(SymText) <> nil then
  373.         error("duplicate id");
  374.     TypeID := EnterStandard(SymText, obtype, BadType, st_none, 0);
  375.     NextSymbol;
  376.     if not Match(equal1) then
  377.         Error("expecting '=' here");
  378.     TypeID^.VType := ReadType();
  379.     ns;
  380.     end;
  381.     TypeID := Nil;
  382. end;
  383.  
  384. Function DeclArgs(ob : IDObject) : IDPtr;
  385.  
  386.     Procedure DeclArgList(var VarList : IDPtr; ob : IDObject);
  387.     var
  388.     ID,
  389.     RunID : IDPtr;
  390.     begin
  391.     if CurrSym = Ident1 then begin
  392.         if CheckIDList(SymText, VarList) then
  393.         error("Duplicate Parameter Name");
  394.         New(ID);
  395.         ID^.Name := EnterSpell(SymText);
  396.         ID^.Object := ob;
  397.         ID^.Next := Nil;
  398.         if VarList = Nil then
  399.         VarList := ID
  400.         else begin
  401.         RunID := VarList;
  402.         while RunID^.Next <> Nil do
  403.             RunID := RunID^.Next;
  404.         RunID^.Next := ID;
  405.          end;
  406.         NextSymbol;
  407.         if Match(Comma1) then begin
  408.         DeclArgList(VarList, ob);
  409.         ID^.VType := ID^.Next^.VType;
  410.         end else begin
  411.         if not Match(colon1) then
  412.             error("Expecting a colon");
  413.         ID^.VType := ReadType();
  414.         end;
  415.         if (ob = valarg) and (ID^.VType^.Object = ob_file) then
  416.         error("Files must be VAR parameters");
  417.     end;
  418.     end;
  419.  
  420. var
  421.     ID : IDPtr;
  422.  
  423. begin
  424.     ID := Nil;
  425.     if ob = field then begin
  426.     While CurrSym = Ident1 do begin
  427.         DeclArgList(ID, field);
  428.         ns;
  429.     end;
  430.     end else begin
  431.     while (CurrSym = Ident1) or (CurrSym = Var1) do begin
  432.         if Match(Var1) then
  433.         DeclArgList(ID, refarg)
  434.         else
  435.         DeclArgList(ID, valarg);
  436.         if CurrSym <> RightParent1 then
  437.         ns;
  438.     end;
  439.     end;
  440.     DeclArgs := ID;
  441. end;
  442.                     
  443. Function DeclVar(ob : IDObject) :  IDPtr;
  444.  
  445. {
  446.     This is used to declare a local or global variable.
  447. }
  448.  
  449. var
  450.     ID,
  451.     NextID : IDPtr;
  452.     TP    : TypePtr;
  453. begin
  454.     if currsym = ident1 then begin
  455.     if CheckID(symtext) <> Nil then
  456.         error("Duplicate id");
  457.     ID := EnterStandard(symtext, ob, BadType, StandardStorage, 0);
  458.     NextSymbol;
  459.     if match(comma1) then begin
  460.         NextID := DeclVar(ob);
  461.         ID^.VType := NextID^.VType;
  462.     end else begin
  463.         if not match(colon1) then
  464.         error("expecting :");
  465.         ID^.VType := ReadType();
  466.     end;
  467.     if ob = local then begin
  468.         StackSpace := StackSpace + ID^.VType^.Size;
  469.         if Odd(StackSpace) and (ID^.VTYpe^.Size <> 1) then
  470.         StackSpace := Succ(StackSpace);
  471.         ID^.Offset := -StackSpace;
  472.     end;
  473.     end else begin
  474.     error("expecting an identifier");
  475.     if CurrSym = Colon1 then
  476.         TP := ReadType()
  477.     else if match(colon1) then
  478.         TP := ReadType();
  479.     end;
  480.     DeclVar := ID;
  481. end;
  482.  
  483. Procedure VarDeclarations;
  484.  
  485. {
  486.     This handles a variable declaration block.
  487. }
  488. var
  489.     ID    : IDPtr;
  490. begin
  491.     While CurrSym = ident1 do begin
  492.     if CurrentBlock^.Level = 1 then begin
  493.         ID := DeclVar(global);
  494.         ns;
  495.     end else begin
  496.         ID := DeclVar(local);
  497.         ns;
  498.     end;
  499.     end;
  500. end;
  501.  
  502. Function TypedConstant(TP : TypePtr) : Integer;
  503. var
  504.     DefineIt : Boolean;
  505.  
  506.     Function TypedOrdinal(TP : TypePtr) : Integer;
  507.     var
  508.     ExprType : TypePtr;
  509.     ExprVal  : Integer;
  510.     begin
  511.     ExprVal := ConExpr(ExprType);
  512.     if DefineIt then
  513.         Writeln(OutFile, '\tdc.', Suffix(TP^.Size), '\t', ExprVal);
  514.     if not TypeCheck(ExprType, TP) then
  515.         Mismatch;
  516.     TypedOrdinal := ExprVal;
  517.     end;
  518.  
  519.     Function TypedArray(TP : TypePtr) : Integer;
  520.     var
  521.     ExprType : TypePtr;
  522.     ExprVal  : Integer;
  523.     Column   : Short;
  524.     Current  : Integer;
  525.     begin
  526.     if TypeCheck(TP^.SubType, CharType) then begin { special }
  527.         ExprVal := ConExpr(ExprType);
  528.         if not TypeCheck(ExprType, TP) then
  529.         MisMatch;
  530.         if DefineIt then
  531.         DumpLitQ(ExprVal);
  532.         LitPtr := ExprVal;
  533.         TypedArray := 1;
  534.     end else if TP^.SubType^.Object = ob_ordinal then begin
  535.         NeedLeftParent;
  536.         Column := 0;
  537.         if DefineIt then
  538.         Write(OutFile, '\tdc.', Suffix(TP^.SubType^.Size), '\t');
  539.         for Current := 1 to TP^.Upper - TP^.Lower + 1 do begin
  540.         ExprVal := ConExpr(ExprType);
  541.         if not TypeCheck(ExprType, TP^.SubType) then
  542.             Mismatch;
  543.         if DefineIt then begin
  544.             if Column > 60 then begin
  545.             Write(OutFile, '\n\tdc.', Suffix(TP^.SubType^.Size), '\t');
  546.             Column := 0;
  547.             end;
  548.             if Column > 0 then
  549.             Write(OutFile, ',');
  550.             Write(OutFile, ExprVal);
  551.             Column := Column + ExprType^.Size * 3;
  552.         end;
  553.         if CurrSym <> RightParent1 then
  554.             if not Match(Comma1) then
  555.             Error("Expecting a comma");
  556.         end;
  557.         if DefineIt then
  558.         Writeln(OutFile);
  559.         NeedRightParent;
  560.         TypedArray := 1;
  561.     end else begin
  562.         NeedLeftParent;
  563.         for Current := 1 to TP^.Upper - TP^.Lower + 1 do begin
  564.         ExprVal := TypedConstant(TP^.SubType);
  565.         if CurrSym <> RightParent1 then
  566.             if not match(Comma1) then
  567.             Error("Expecting a comma");
  568.         end;
  569.         NeedRightParent;
  570.         TypedArray := 1;
  571.     end;
  572.     end;
  573.  
  574.     Function TypedPointer(TP : TypePtr) : Integer;
  575.     var
  576.     ID : IDPtr;
  577.     ExprVal : Integer;
  578.     ExprType : TypePtr;
  579.     begin
  580.     if Match(At1) then begin
  581.         if CurrSym = Ident1 then begin
  582.         ID := FindID(SymText);
  583.         if (ID^.Object = Global) or
  584.            (ID^.Object = typed_const) then begin
  585.             if DefineIt then begin
  586.             if ID^.Level <= 1 then
  587.                 Writeln(OutFile, '\tdc.l\t_', ID^.Name)
  588.             else
  589.                 Writeln(OutFile, '\tdc.l\t_', ID^.Name,
  590.                         '%', ID^.Unique);
  591.             end;
  592.             if not TypeCheck(TP^.SubType, ID^.VType) then
  593.             MisMatch;
  594.         end else
  595.             Error("Expecting a global identifier");
  596.         NextSymbol;
  597.         end else
  598.         Error("Expecting an identifier");
  599.         TypedPointer := 1;
  600.     end else begin
  601.         ExprVal := ConExpr(ExprType);
  602.         if not TypeCheck(ExprType, TP) then
  603.         Mismatch;
  604.         if DefineIt then begin
  605.         if ExprType = StringType then begin
  606.             Write(OutFile, '\tdc.l\t');
  607.             PrintLabel(LitLab);
  608.             Writeln(OutFile, '+', ExprVal);
  609.         end else
  610.             Writeln(OutFile, '\tdc.l\t', ExprVal);
  611.         end else
  612.         LitPtr := ExprVal;
  613.         TypedPointer := ExprVal;
  614.     end;
  615.     end;
  616.  
  617.     Function TypedRecord(TP : TypePtr) : Integer;
  618.     var
  619.     ID : IDPtr;
  620.     ExprVal : Integer;
  621.     begin
  622.     NeedLeftParent;
  623.     ID := TP^.Ref;
  624.     while ID <> Nil do begin
  625.         ExprVal := TypedConstant(ID^.VType);
  626.         ID := ID^.Next;
  627.         if ID <> Nil then
  628.         if not Match(Comma1) then
  629.             Error("Expecting a comma");
  630.     end;
  631.     NeedRightParent;
  632.     TypedRecord := 1;
  633.     end;
  634.  
  635.     Function TypedReal : Integer;
  636.     var
  637.     ExprVal : Integer;
  638.     ExprType : TypePtr;
  639.     begin
  640.     ExprVal := ConExpr(ExprType);
  641.     if not TypeCheck(ExprType, RealType) then
  642.         MisMatch;
  643.     if DefineIt then begin
  644.         Write(OutFile, '\tdc.l\t');
  645.         WriteHex(ExprVal);
  646.         Writeln(OutFile);
  647.     end;
  648.     TypedReal := ExprVal;
  649.     end;
  650.  
  651. begin
  652.     DefineIt := StandardStorage <> st_external;
  653.     case TP^.Object of
  654.     ob_ordinal,
  655.     ob_subrange : TypedConstant := TypedOrdinal(TP);
  656.     ob_array   : TypedConstant := TypedArray(TP);
  657.     ob_pointer : TypedConstant := TypedPointer(TP);
  658.     ob_record  : TypedConstant := TypedRecord(TP);
  659.     ob_real    : TypedConstant := TypedReal;
  660.     else
  661.     Error("No typed constants allowed for this type");
  662.     end;
  663. end;
  664.  
  665. Procedure DeclConst;
  666.  
  667. {
  668.     This handles a const declaration block.  The grunt work is
  669. does by conexpr() in expression.p, which is the routine to look at
  670. if you want to improve constant declarations.
  671. }
  672. var
  673.     ID : IDPtr;
  674.     BackName : String;
  675.     TP : TypePtr;
  676. begin
  677.     While CurrSym = Ident1 do begin
  678.     if CheckID(SymText) <> Nil then
  679.         Error("Duplicate ID");
  680.     ID := EnterStandard(SymText, constant, Nil, st_none, 0);
  681.     BackName := ID^.Name;
  682.     ID^.Name := "";    { So the ID can't be used in the expression }
  683.     NextSymbol;
  684.     if Match(Colon1) then begin
  685.         ID^.VType := ReadType;
  686.         if not Match(Equal1) then
  687.         Error("Missing =");
  688.         if StandardStorage <> st_external then begin
  689.         if ID^.VType^.Size > 1 then
  690.             Writeln(OutFile, '\tCNOP\t0,2');
  691.         if CurrentBlock^.Level <= 1 then begin
  692.             if StandardStorage <> st_private then
  693.             Writeln(OutFile, '\tXDEF\t_', BackName);
  694.             writeln(OutFile, '_', BackName)
  695.         end else begin
  696.             ID^.Unique := GetLabel;
  697.             writeln(OutFile, '_', BackName, '%', ID^.Unique);
  698.         end;
  699.         end;
  700.         ID^.Offset := TypedConstant(ID^.VType);
  701.         ID^.Name := BackName;
  702.         ID^.Object := typed_const;
  703.         if StandardStorage <> st_external then
  704.         ID^.Storage := st_initialized
  705.         else
  706.         ID^.Storage := st_external;
  707.     end else begin
  708.         if not Match(Equal1) then
  709.         Error("Expecting =");
  710.         ID^.Offset := ConExpr(TP);
  711.         ID^.VType := TP;
  712.         ID^.Name := BackName;
  713.     end;
  714.     ns;
  715.     end;
  716. end;
  717.  
  718. Procedure DeclLabel;
  719. {
  720.     This routine accepts a list of identifiers to be used as
  721.     labels in the program.  Standard Pascal's labels are four
  722.     digit numbers, but I didn't want to mess with that.
  723. }
  724. var
  725.     ID : IDPtr;
  726. begin
  727.     while CurrSym = Ident1 do begin
  728.     ID := EnterStandard(SymText, lab, Nil, st_none, 0);
  729.     ID^.Unique := GetLabel;
  730.     NextSymbol;
  731.     if not Match(Comma1) then begin
  732.         ns;
  733.         return;
  734.     end;
  735.     end;
  736.     Error("Expecting an identifier");
  737. end;
  738.